package com.meis.widget.heart;

import com.meis.widget.ResourceTable;
import com.meis.widget.utils.AttrUtils;
import com.meis.widget.utils.DensityUtil;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.*;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Point;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.utils.PlainArray;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class MeiHeartView extends Component implements Component.BindStateChangedListener, Component.EstimateSizeListener, Component.DrawTask {
    //默认动画时长
    private static final int DURATION_TIME = 3000;
    //动画时长
    private int mDuration;
    //是否控制透明度速率
    private boolean mAlphaEnable;
    //是否控制缩放
    private boolean mScaleEnable;
    private float mScreenOffset;
    private final SecureRandom mRandom = new SecureRandom();
    private PlainArray<PixelMap> mPixelMapArray = new PlainArray<>();
    private PlainArray<Heart> mHeartArray = new PlainArray<>();
    private List<Animator> mAnimatorList = new ArrayList<>();
    private Paint mPaint;
    private Matrix mMatrix;
    private int mComponentWidth;
    private int mComponentHeight;

    public MeiHeartView(Context context) {
        this(context, null);
    }

    public MeiHeartView(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public MeiHeartView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        //关闭硬件加速
        /*setLayerType(LAYER_TYPE_SOFTWARE, null);*/
        if (attrSet != null) {
            mDuration = AttrUtils.getIntegerFromAttr(attrSet, "heartDuration", DURATION_TIME);
            mAlphaEnable = AttrUtils.getBooleanFromAttr(attrSet, "heartEnableAlpha", true);
            mScaleEnable = AttrUtils.getBooleanFromAttr(attrSet, "heartEnableScale", true);
            mScreenOffset = DensityUtil.getDimensionPixelSize(mContext, ResourceTable.String_toolbar_height);
        }
        init();
    }

    private void init() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mMatrix = new Matrix();

        setBindStateChangedListener(this);
        setEstimateSizeListener(this);
        addDrawTask(this);
    }

    /**
     * 设置爱心资源图片
     * @param type 爱心类型
     * @param bitmap 资源图片
     */
    public void setHeartBitmap(int type, PixelMap bitmap) {
        if (!mPixelMapArray.get(type).isPresent()) {
            mPixelMapArray.put(type, bitmap);
        }
    }

    /**
     * 设置爱心资源图片
     * @param bitmapArray 资源图片数组
     */
    public void setHeartBitmap(PlainArray<PixelMap> bitmapArray) {
        if (null != bitmapArray) {
            mPixelMapArray = bitmapArray;
        }
    }

    private void addHeart(final int index) {
        if (mPixelMapArray.size() == 0 || index < 0 || index > (mPixelMapArray.size() - 1)) {
            return;
        }

        final Path path = new Path();
        final PathMeasure pathMeasure = new PathMeasure(null, false);
        final Heart heart = new Heart();
        heart.index = index;

        //绘制三阶贝塞尔曲线 起点位置
        Point start = new Point();
        //贝塞尔控制点1
        Point control1 = new Point();
        //贝塞尔控制点2
        Point control2 = new Point();
        //贝塞尔结束点
        Point end = new Point();

        initStartAndEnd(start, end);
        initControl(control1, control2);

        path.moveTo(start.getPointX(), start.getPointY());
        path.cubicTo(control1.getPointX(), control1.getPointY(), control2.getPointX(), control2.getPointY(), end.getPointX(), end.getPointY());
        pathMeasure.setPath(path, false);
        final float pathLength = pathMeasure.getLength();

        AnimatorValue animator = new AnimatorValue();
        //动画的长短来控制速率
        animator.setDuration(mDuration);
        //先加速后减速
        animator.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        animator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                MeiHeartView.this.onUpdate(heart, pathMeasure, v, pathLength);
            }
        });
        animator.setStateChangedListener(new SimpleAnimatorStateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                MeiHeartView.this.onStart(heart, animator);
            }

            @Override
            public void onEnd(Animator animator) {
                MeiHeartView.this.onEnd(heart, animator);
            }
        });
        animator.start();
    }

    private void onUpdate(Heart heart, PathMeasure pathMeasure, float v, float pathLength) {
        /*float[] pos = new float[2];
        pathMeasure.getPosTan(v * pathLength, pos, null);*/

        Matrix matrix = new Matrix();
        pathMeasure.getMatrix(v * pathLength, matrix, PathMeasure.GET_POSITION_MATRIX_FLAG);
        float[] data = matrix.getData();
        heart.x = data[2];
        heart.y = data[5];
        heart.progress = v;
        invalidate();
    }

    private void onStart(Heart heart, Animator animator) {
        int hashCode = heart.hashCode();

        if (!mHeartArray.contains(hashCode)) {
            mHeartArray.put(hashCode, heart);
        }

        if (!mAnimatorList.contains(animator)) {
            mAnimatorList.add(animator);
        }
    }

    private void onEnd(Heart heart, Animator animator) {
        int hashCode = heart.hashCode();

        if (mHeartArray.contains(hashCode)) {
            mHeartArray.remove(hashCode);
        }

        mAnimatorList.remove(animator);
        animator.stop();
        animator.release();
    }

    private abstract static class SimpleAnimatorStateChangedListener implements Animator.StateChangedListener {

        @Override
        public void onStop(Animator animator) {
        }

        @Override
        public void onCancel(Animator animator) {
        }

        @Override
        public void onPause(Animator animator) {
        }

        @Override
        public void onResume(Animator animator) {
        }
    }

    public void addHeart() {
        if (mPixelMapArray.size() > 0) {
            addHeart(mRandom.nextInt(mPixelMapArray.size() - 1));
        }
    }

    /**
     * 绘制爱心
     * @param canvas canvas
     */
    private void canvasHeart(Canvas canvas) {
        int size = mHeartArray.size();
        for (int i = 0; i < size; i++) {
            Heart heart = mHeartArray.valueAt(i);

            if (null == heart) {
                return;
            }

            //设置透明度，从1.0到0.0，前面80%都是1.0，后面20%开始从1.0到0.0
            float alpha = dealToAlpha(heart.progress);
            mPaint.setAlpha(alpha);

            mMatrix.reset();
            //会覆盖掉之前的x,y数值
            mMatrix.setTranslate(0, 0);
            //位移到x,y
            mMatrix.postTranslate(heart.x, heart.y);

            // 设置缩放，有中心点，从0.5到1.0，前面10%从0.5到1.0，后面90%都是1.0
            float scale = dealToScale(heart.progress);
            mMatrix.postScale(scale, scale, (float) mComponentWidth / 2, (float) mComponentHeight + mScreenOffset);

            canvas.setMatrix(mMatrix);
            Optional<PixelMap> pixelMapOptional = mPixelMapArray.get(heart.index);

            if (pixelMapOptional.isPresent()) {
                canvas.drawPixelMapHolder(new PixelMapHolder(pixelMapOptional.get()), 0, 0, mPaint);
                canvas.resetMatrix();
            }
        }
    }

    /**
     * 根据动画进度获取透明度
     * @param fraction 动画进度
     * @return 透明度(0.0f ~ 1.0f)
     */
    private float dealToAlpha(float fraction) {
        if (fraction > 0.8f && fraction <= 1.0f && mAlphaEnable) {
            return 1.0f - fraction + (fraction > 0.9f ? 0.1f : 0.2f);
        }
        return 1.0f;
    }

    /**
     * 根据动画进度获取缩放值
     * @param fraction 动画进度
     * @return 缩放值(0.5f ~ 1.0f)
     */
    private float dealToScale(float fraction) {
        if (fraction < 0.1f && mScaleEnable) {
            return 0.5f + fraction / 0.1f * 0.5f;
        }
        return 1.0f;
    }

    /**
     * 设置控制点
     * @param control1 控制点1
     * @param control2 控制点2
     */
    private void initControl(Point control1, Point control2) {
        float x1 = (float) (mRandom.nextDouble() * mComponentWidth);
        float y1 = (float) (mRandom.nextDouble() * (mComponentHeight + mScreenOffset));
        float x2 = (float) (mRandom.nextDouble() * mComponentWidth);
        float y2 = (float) (mRandom.nextDouble() * (mComponentHeight + mScreenOffset));
        control1.modify(x1, y1);
        control2.modify(x2, y2);

        if (control1.getPointX() == control2.getPointX() && control1.getPointY() == control2.getPointY()) {
            initControl(control1, control2);
        }
    }

    /**
     * 设置起点和终点，注意有偏移量
     * @param start 起点
     * @param end 终点
     */
    private void initStartAndEnd(Point start, Point end) {
        start.modify((float) mComponentWidth / 2, mComponentHeight + mScreenOffset);
        end.modify((float) mComponentWidth / 2, mScreenOffset);
    }

    /**
     * 释放图片资源
     */
    public void bitmapCycle() {
        //回收bitmap
        for (int i = 0; i < mPixelMapArray.size(); i++) {
            if (mPixelMapArray.valueAt(i) != null) {
                mPixelMapArray.valueAt(i).release();
            }
        }
    }

    /**
     * 取消已有动画，释放动画资源
     */
    public void releaseAnimator() {
        if (!mAnimatorList.isEmpty()) {
            for (Animator animator : mAnimatorList) {
                animator.stop();
                animator.release();
            }
            mAnimatorList.clear();
        }
    }

    @Override
    public void onComponentBoundToWindow(Component component) {

    }

    @Override
    public void onComponentUnboundFromWindow(Component component) {
        bitmapCycle();
        removeBindStateChangedListener(this);
    }

    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = EstimateSpec.getMode(widthMeasureSpec);
        int heightMode = EstimateSpec.getMode(heightMeasureSpec);
        int widthSize = EstimateSpec.getSize(widthMeasureSpec);
        int heightSize = EstimateSpec.getSize(heightMeasureSpec);
        int warpDefaultSize = DensityUtil.dp2px(getContext(), 100);

        if (widthMode == EstimateSpec.NOT_EXCEED && heightMode == EstimateSpec.NOT_EXCEED) {
            widthSize = heightSize = warpDefaultSize;
        } else if (widthMode == EstimateSpec.NOT_EXCEED) {
            widthSize = warpDefaultSize;
        } else if (heightMode == EstimateSpec.NOT_EXCEED) {
            heightSize = warpDefaultSize;
        }

        mComponentWidth = widthSize;
        mComponentHeight = heightSize;
        setEstimatedSize(widthSize, heightSize);
        return false;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        canvasHeart(canvas);
    }
}
